---
title: Agent State
icon: "lucide/Bot"
description: Render the state of your agent with custom UI components.
---
import { Accordions, Accordion } from "fumadocs-ui/components/accordion";
import { IframeSwitcher } from "@/components/content"
import { Tabs, Tab } from "fumadocs-ui/components/tabs"
import RunAndConnect from "@/snippets/integrations/langgraph/run-and-connect.mdx"

<IframeSwitcher
  id="agent-state-example"
  exampleUrl="https://feature-viewer.copilotkit.ai/langgraph/feature/agentic_generative_ui?sidebar=false&chatDefaultOpen=false"
  codeUrl="https://feature-viewer.copilotkit.ai/langgraph/feature/agentic_generative_ui?view=code&sidebar=false&codeLayout=tabs"
  exampleLabel="Demo"
  codeLabel="Code"
  height="700px"
/>

## What is this?

All LangGraph agents are stateful. This means that as your agent progresses through nodes, a state object is passed between them perserving
the overall state of a session. CopilotKit allows you to render this state in your application with custom UI components, which we call **Agentic Generative UI**.

## When should I use this?

Rendering the state of your agent in the UI is useful when you want to provide the user with feedback about the overall state of a session. A great example of this
is a situation where a user and an agent are working together to solve a problem. The agent can store a draft in its state which is then rendered in the UI.

## Implementation

<Steps>
  <Step>
    ### Run and connect your agent
    <RunAndConnect components={props.components} />
  </Step>
  <Step>
    ### Define your agent state
    LangGraph agents are [stateful](https://langchain-ai.github.io/langgraph/concepts/low_level/). As you progress through nodes, a state object is passed between them. CopilotKit
    allows you to easily render this state in your application.

    For the sake of this guide, let's say our state looks like this in our agent.

    <Tabs groupId="language_langgraph_agent" items={['Python', 'TypeScript']} default="Python" persist>
        <Tab value="Python">
          ```python title="agent.py"
          from copilotkit import CopilotKitState # extends MessagesState

          # This is the state of the agent, we inherit from CopilotKitState to bind in
          # useful helpers when interacting with the agent via CopilotKit.
          class AgentState(CopilotKitState):
              searches: list[dict]
          ```
        </Tab>
        <Tab value="TypeScript">
          ```typescript title="agent-js/src/agent.ts"
          import { Annotation } from "@langchain/langgraph";
          import { CopilotKitStateAnnotation } from "@copilotkit/sdk-js/langgraph";

          // This is the state of the agent, we inherit from CopilotKitState to bind in
          // useful helpers when interacting with the agent via CopilotKit.
          export const AgentStateAnnotation = Annotation.Root({
            searches: Annotation<object[]>,
            ...CopilotKitStateAnnotation.spec,
          });
          export type AgentState = typeof AgentStateAnnotation.State;
          ```
        </Tab>
    </Tabs>
  </Step>
  <Step>
    ### Simulate state updates
    Next, let's write some logic into our agent that will simulate state updates occurring.

    <Tabs groupId="language_langgraph_agent" items={['Python', 'TypeScript']} default="Python" persist>
      <Tab value="Python">
        ```python title="agent.py"
        import asyncio
        from typing import TypedDict
        from langchain_core.runnables import RunnableConfig
        from langchain_openai import ChatOpenAI
        from langchain_core.messages import SystemMessage
        from copilotkit import CopilotKitState
        from copilotkit.langgraph import copilotkit_emit_state # [!code highlight]

        class Searches(TypedDict):
            query: str
            done: bool

        class AgentState(CopilotKitState):
            searches: list[Searches] = []

        async def chat_node(state: AgentState, config: RunnableConfig):
            state["searches"] = [
                {"query": "Initial research", "done": False},
                {"query": "Retrieving sources", "done": False},
                {"query": "Forming an answer", "done": False},
            ]

            # [!code highlight:3]
            # We can call copilotkit_emit_state to emit updated state
            # before a node finishes
            await copilotkit_emit_state(config, state)

            # Simulate state updates
            for search in state["searches"]:
                await asyncio.sleep(1)
                search["done"] = True

                # [!code highlight:2]
                # We can also emit updates in a loop to simulate progress
                await copilotkit_emit_state(config, state)

            # Run the model to generate a response
            response = await ChatOpenAI(model="gpt-4o").ainvoke([
                SystemMessage(content="You are a helpful assistant."),
                *state["messages"],
            ], config)
        ```
      </Tab>
      <Tab value="TypeScript">
        ```typescript title="agent-js/src/agent.ts"
        import { RunnableConfig } from "@langchain/core/runnables";
        import { ChatOpenAI } from "@langchain/openai";
        import { Annotation } from "@langchain/langgraph";
        import { SystemMessage } from "@langchain/core/messages";
        import { copilotkitEmitState, CopilotKitStateAnnotation } from "@copilotkit/sdk-js/langgraph";

        type Search = {
          query: string;
          done: boolean;
        }

        export const AgentStateAnnotation = Annotation.Root({
          searches: Annotation<Search[]>,
          ...CopilotKitStateAnnotation.spec,
        });

        async function chat_node(state: AgentState, config: RunnableConfig) {
          state.searches = [
            { query: "Initial research", done: false },
            { query: "Retrieving sources", done: false },
            { query: "Forming an answer", done: false },
          ];

          // [!code highlight:3]
          // We can call copilotkit_emit_state to emit updated state
          // before a node finishes
          await copilotkitEmitState(config, state);

          // Simulate state updates
          for (const search of state.searches) {
            await new Promise(resolve => setTimeout(resolve, 1000));
            search.done = true;

            // [!code highlight:2]
            // We can also emit updates in a loop to simulate progress
            await copilotkitEmitState(config, state);
          }

          const response = await new ChatOpenAI({ model: "gpt-4o" }).invoke([
            new SystemMessage({ content: "You are a helpful assistant."}),
            ...state.messages,
          ], config);
        ```
      </Tab>
    </Tabs>
  </Step>
  <Step>
    ### Render state of the agent in the chat
    Now we can utilize `useCoAgentStateRender` to render the state of our agent **in the chat**.

    ```tsx title="app/page.tsx"
    import { useCoAgentStateRender } from "@copilotkit/react-core";

    // For type safety, redefine the state of the agent. If you're using
    // using LangGraph JS you can import the type here and share it.
    type AgentState = {
      searches: {
        query: string;
        done: boolean;
      }[];
    };

    function YourMainContent() {
      // ...

      // [!code highlight:13]
      // styles omitted for brevity
      useCoAgentStateRender<AgentState>({
        name: "sample_agent", // the name the agent is served as
        render: ({ state }) => (
          <div>
            {state.searches?.map((search, index) => (
              <div key={index}>
                {search.done ? "✅" : "❌"} {search.query}{search.done ? "" : "..."}
              </div>
            ))}
          </div>
        ),
      });

      // ...

      return <div>...</div>;
    }
    ```
  </Step>
  <Step>
    ### Render state outside of the chat
    You can also render the state of your agent **outside of the chat**. This is useful when you want to render the state of your agent anywhere
    other than the chat.

    ```tsx title="app/page.tsx"
    import { useCoAgent } from "@copilotkit/react-core"; // [!code highlight]
    // ...

    // Define the state of the agent, should match the state of the agent in your LangGraph.
    type AgentState = {
      searches: {
        query: string;
        done: boolean;
      }[];
    };

    function YourMainContent() {
      // ...

      // [!code highlight:3]
      const { state } = useCoAgent<AgentState>({
        name: "sample_agent", // the name the agent is served as
      })

      // ...

      return (
        <div>
          {/* ... */}
          <div className="flex flex-col gap-2 mt-4">
            {/* [!code highlight:5] */}
            {state.searches?.map((search, index) => (
              <div key={index} className="flex flex-row">
                {search.done ? "✅" : "❌"} {search.query}
              </div>
            ))}
          </div>
        </div>
      )
    }
    ```
  </Step>
  <Step>
    ### Give it a try!

    You've now created a component that will render the agent's state in the chat.
  </Step>
</Steps>
